use super::*;

mod dataview;
mod typedbuffer;

pub use dataview::*;
pub use typedbuffer::*;

/// Represents a JavaScript ArrayBuffer object
#[derive(Clone, Copy)]
pub struct ArrayBuffer<'val> {
	pub(crate) env: &'val Env,
	pub(crate) inner: sys::napi_value,
}

impl<'val> ArrayBuffer<'val> {
	/// Creates a new ArrayBuffer with the specified size
	pub fn new<'env>(env: &'env Env, size: usize) -> Result<'static, (ArrayBuffer<'env>, &'env [u8])> {
		let mut ret = core::ptr::null_mut();
		let mut data = core::ptr::null_mut();
		unsafe { sys::napi_create_arraybuffer(env.raw(), size as _, &mut data, &mut ret) } //
			.result("napi_create_arraybuffer")?;
		Ok((ArrayBuffer { env, inner: ret }, unsafe {
			core::slice::from_raw_parts_mut(data as *mut u8, size as usize)
		}))
	}

	/// Creates a new ArrayBuffer with the specified type and size
	pub fn with_type<'env>(env: &'env Env, element_type: TypedArrayType, size: usize) -> Result<'static, (ArrayBuffer<'env>, &'env [u8])> {
		let size = size * element_type.data_size();
		ArrayBuffer::new(env, size)
	}

	/// Creates an ArrayBuffer from a raw napi_value
	pub fn with_raw<'env>(env: &'env Env, raw: sys::napi_value) -> ArrayBuffer<'env> {
		ArrayBuffer { env, inner: raw }
	}

	/// Converts ArrayBuffer to a generic Value
	pub fn v(&'_ self) -> Value<'val> {
		Value::new(self.env, self.inner)
	}

	/// Gets the underlying byte array and its size
	pub fn info(&'_ self) -> Result<'static, &'_ [u8]> {
		let mut size = 0;
		let mut data = core::ptr::null_mut();
		unsafe { sys::napi_get_arraybuffer_info(self.env.raw(), self.inner, &mut data, &mut size) } //
			.result("napi_get_arraybuffer_info")?;
		Ok(unsafe { core::slice::from_raw_parts_mut(data as *mut u8, size as usize) })
	}

	/// Detaches the ArrayBuffer from its backing store
	pub fn detach(&'_ self) -> Result<'static, ()> {
		unsafe { sys::napi_detach_arraybuffer(self.env.raw(), self.inner) } //
			.result("napi_detach_arraybuffer")?;
		Ok(())
	}

	/// Checks if the ArrayBuffer is detached
	pub fn is_detached(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_detached_arraybuffer(self.env.raw(), self.inner, &mut ret) } //
			.result("napi_is_detached_arraybuffer")?;
		Ok(ret)
	}
}

impl<'val> FromNapi<'val> for ArrayBuffer<'val> {
	/// Converts from Value to ArrayBuffer
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		if v.is_arraybuffer()? {
			Ok(ArrayBuffer {
				env: v.env,
				inner: v.inner,
			})
		} else {
			Err(Status::invalid_arg("value is not ArrayBuffer type"))
		}
	}
}

impl<'val> IntoNapi for ArrayBuffer<'val> {
	/// Converts from ArrayBuffer to Value
	fn into_napi<'e>(self, env: &'e Env) -> Result<'static, Value<'e>> {
		if self.env.raw() != env.raw() {
			return Err(Status::invalid_arg("self.env not eq arg env"));
		}
		Ok(Value::new(env, self.inner))
	}
}
